<?php
declare(strict_types=1);

namespace SixShop\MakerBundle\Generator;

use Symfony\Component\Console\Style\SymfonyStyle;
use Phinx\Util\Util;

class MigrationGenerator
{
    private string $migrationsPath;
    private array $fieldTypes = [
        'integer' => 'integer',
        'biginteger' => 'bigInteger',
        'string' => 'string',
        'text' => 'text',
        'boolean' => 'boolean',
        'datetime' => 'datetime',
        'timestamp' => 'timestamp',
        'date' => 'date',
        'time' => 'time',
        'decimal' => 'decimal',
        'float' => 'float',
        'binary' => 'binary',
        'json' => 'json'
    ];

    public function __construct(?string $migrationsPath = null)
    {
        $this->migrationsPath = $migrationsPath ?: getcwd() . '/database/migrations';
    }

    /**
     * Generate migration file based on table definition
     */
    public function generateMigration(string $tableName, array $fields = [], string $action = 'create', ?SymfonyStyle $io = null): bool
    {
        try {
            // Ensure migrations directory exists
            if (!is_dir($this->migrationsPath)) {
                if (!mkdir($this->migrationsPath, 0755, true)) {
                    if ($io) $io->error("无法创建迁移目录: {$this->migrationsPath}");
                    return false;
                }
            }

            // Generate migration name and class name
            $migrationName = $this->generateMigrationName($tableName, $action);
            $className = $this->generateClassName($migrationName);
            
            // Generate timestamp
            $timestamp = date('YmdHis');
            $filename = $timestamp . '_' . $migrationName . '.php';
            $filePath = $this->migrationsPath . '/' . $filename;

            // Generate migration content
            $content = $this->generateMigrationContent($className, $tableName, $fields, $action);

            // Write migration file
            if (file_put_contents($filePath, $content) !== false) {
                if ($io) $io->success("Migration created: {$filename}");
                return true;
            } else {
                if ($io) $io->error("无法写入迁移文件: {$filename}");
                return false;
            }

        } catch (\Exception $e) {
            if ($io) $io->error('生成迁移文件时发生错误: ' . $e->getMessage());
            return false;
        }
    }

    /**
     * Generate migration name
     */
    private function generateMigrationName(string $tableName, string $action): string
    {
        switch ($action) {
            case 'create':
                return 'create_' . $tableName . '_table';
            case 'add_column':
                return 'add_columns_to_' . $tableName . '_table';
            case 'drop_column':
                return 'drop_columns_from_' . $tableName . '_table';
            case 'modify':
                return 'modify_' . $tableName . '_table';
            default:
                return 'update_' . $tableName . '_table';
        }
    }

    /**
     * Generate migration class name
     */
    private function generateClassName(string $migrationName): string
    {
        return Util::mapFileNameToClassName($migrationName);
    }

    /**
     * Generate migration file content
     */
    private function generateMigrationContent(string $className, string $tableName, array $fields, string $action): string
    {
        $content = "<?php\n";
        $content .= "declare(strict_types=1);\n\n";
        $content .= "use Phinx\\Migration\\AbstractMigration;\n\n";
        $content .= "final class {$className} extends AbstractMigration\n";
        $content .= "{\n";
        
        // Generate change method
        $content .= "    /**\n";
        $content .= "     * Change Method.\n";
        $content .= "     *\n";
        $content .= "     * Write your reversible migrations using this method.\n";
        $content .= "     *\n";
        $content .= "     * More information on writing migrations is available here:\n";
        $content .= "     * https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method\n";
        $content .= "     *\n";
        $content .= "     * Remember to call \"create()\" or \"update()\" and NOT \"save()\" when working\n";
        $content .= "     * with the Table class.\n";
        $content .= "     */\n";
        $content .= "    public function change(): void\n";
        $content .= "    {\n";
        
        switch ($action) {
            case 'create':
                $content .= $this->generateCreateTableContent($tableName, $fields);
                break;
            case 'add_column':
                $content .= $this->generateAddColumnContent($tableName, $fields);
                break;
            case 'drop_column':
                $content .= $this->generateDropColumnContent($tableName, $fields);
                break;
            default:
                $content .= $this->generateModifyTableContent($tableName, $fields);
                break;
        }
        
        $content .= "    }\n";
        $content .= "}\n";

        return $content;
    }

    /**
     * Generate create table content
     */
    private function generateCreateTableContent(string $tableName, array $fields): string
    {
        $content = "        \$table = \$this->table('{$tableName}');\n";
        
        if (!empty($fields)) {
            foreach ($fields as $field) {
                $fieldName = $field['name'];
                $fieldType = $this->fieldTypes[$field['type']] ?? 'string';
                $options = $this->buildFieldOptions($field);
                
                $content .= "        \$table->addColumn('{$fieldName}', '{$fieldType}'";
                if (!empty($options)) {
                    $content .= ", " . $this->arrayToString($options);
                }
                $content .= ");\n";
            }
        }

        // Add indexes if specified
        foreach ($fields as $field) {
            if (!empty($field['index'])) {
                $content .= "        \$table->addIndex(['{$field['name']}']);\n";
            }
            if (!empty($field['unique'])) {
                $content .= "        \$table->addIndex(['{$field['name']}'], ['unique' => true]);\n";
            }
        }

        $content .= "        \$table->create();\n";
        
        return $content;
    }

    /**
     * Generate add column content
     */
    private function generateAddColumnContent(string $tableName, array $fields): string
    {
        $content = "        \$table = \$this->table('{$tableName}');\n";
        
        foreach ($fields as $field) {
            $fieldName = $field['name'];
            $fieldType = $this->fieldTypes[$field['type']] ?? 'string';
            $options = $this->buildFieldOptions($field);
            
            $content .= "        \$table->addColumn('{$fieldName}', '{$fieldType}'";
            if (!empty($options)) {
                $content .= ", " . $this->arrayToString($options);
            }
            $content .= ");\n";
        }
        
        $content .= "        \$table->update();\n";
        
        return $content;
    }

    /**
     * Generate drop column content
     */
    private function generateDropColumnContent(string $tableName, array $fields): string
    {
        $content = "        \$table = \$this->table('{$tableName}');\n";
        
        foreach ($fields as $field) {
            $fieldName = is_array($field) ? $field['name'] : $field;
            $content .= "        \$table->removeColumn('{$fieldName}');\n";
        }
        
        $content .= "        \$table->update();\n";
        
        return $content;
    }

    /**
     * Generate modify table content
     */
    private function generateModifyTableContent(string $tableName, array $fields): string
    {
        $content = "        \$table = \$this->table('{$tableName}');\n";
        $content .= "        // Add your table modifications here\n";
        $content .= "        \$table->update();\n";
        
        return $content;
    }

    /**
     * Build field options array
     */
    private function buildFieldOptions(array $field): array
    {
        $options = [];
        
        if (isset($field['length'])) {
            $options['limit'] = (int)$field['length'];
        }
        
        if (isset($field['null'])) {
            $options['null'] = (bool)$field['null'];
        } else {
            $options['null'] = true; // Default to nullable
        }
        
        if (isset($field['default'])) {
            $options['default'] = $field['default'];
        }
        
        if (isset($field['comment'])) {
            $options['comment'] = $field['comment'];
        }
        
        if (isset($field['after'])) {
            $options['after'] = $field['after'];
        }
        
        // Handle precision and scale for decimal fields
        if ($field['type'] === 'decimal') {
            if (isset($field['precision'])) {
                $options['precision'] = (int)$field['precision'];
            }
            if (isset($field['scale'])) {
                $options['scale'] = (int)$field['scale'];
            }
        }
        
        return $options;
    }

    /**
     * Convert array to string representation
     */
    private function arrayToString(array $array): string
    {
        $parts = [];
        foreach ($array as $key => $value) {
            if (is_string($value)) {
                $parts[] = "'{$key}' => '{$value}'";
            } elseif (is_bool($value)) {
                $parts[] = "'{$key}' => " . ($value ? 'true' : 'false');
            } elseif (is_null($value)) {
                $parts[] = "'{$key}' => null";
            } else {
                $parts[] = "'{$key}' => {$value}";
            }
        }
        return '[' . implode(', ', $parts) . ']';
    }

    /**
     * Get supported field types
     */
    public function getSupportedFieldTypes(): array
    {
        return array_keys($this->fieldTypes);
    }

    /**
     * Set migrations path
     */
    public function setMigrationsPath(string $path): void
    {
        $this->migrationsPath = $path;
    }

    /**
     * Get migrations path
     */
    public function getMigrationsPath(): string
    {
        return $this->migrationsPath;
    }
}